package com.ppch.wuwamanus.agent;

import cn.hutool.core.util.StrUtil;
import com.google.common.hash.Hashing;
import com.ppch.wuwamanus.agent.model.AgentState;
import com.ppch.wuwamanus.agent.model.ReasonData;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.MessageType;
import org.springframework.util.Assert;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * Package:ppch-ai-agent
 * ClassName:ReActAgent
 *
 * @Author 泡泡茶壶
 * @Create 2025/7/30 21:00
 * @Version 1.0
 * Description:
 *  Reasoning+Acting：实现先思考 -> 行动 ->观察 ->循环迭代 模式
 *  1.推理(Reason):将原始问题拆分为多步骤任务，明确当前要执行的步骤
 *  2.行动(Act):调用外部工具执行动作
 *  3.观察(Observe):获取工具返回的结果，反馈给智能体进行下一步决策
 *  4.循环迭代:不断重复上述3个过程，直到任务完成或达到终止条件。
 */
@EqualsAndHashCode(callSuper = true)
@Data
@Slf4j
public abstract class ReActAgent extends BaseAgent{

    /**
     * 重复执行阈值（相同操作重复执行多少次则判定智能体陷入死循环状态）
     */
    private int duplicateThreshold = 1;

    /**
     * 检查智能体是否陷入循环
     * TODO：如何中止智能体循环？
     *      直接从 List<String> resultList 中获取 AI 响应结果集合，遍历集合，并将每一条结果与最新的一条结果进行比较，
     *      如果内容相同则表示陷入循环，设置 setNextStepPrompt 让 AI 终止回合，（带有随机性，AI 可能并不会立即终止）
     *      所以这里采用设置 setNextStepPrompt + 设置执行状态位 AgentState.FINISHED + 设置最大执行步骤 三种方式结合，确保 AI 终止循环
     * TODO: 如何避免发送重复的响应给前端？（在智能体陷入循环时就会产出重复响应）
     *      在 ToolCallAgent 类中的 runReactiveStream()方法中在将AI响应结果发送给前端前，先获取到AI 响应结果集合 List<String> resultList，
     *      并判断当前执行结果与上一步执行结果是否相同，如果相同则跳过当前执行结果（置为 ""），避免发送重复数据。
     *      （结果集合必须大于 1，避免把第一次的响应数据也给置为空了。）
     *       if(resultList.size() > 1){
     *                         String preResult = resultList.get(resultList.size() - 2);
     *                         if(StrUtil.equals(currentStepResult,preResult)){
     *                             log.warn("将重复响应置空，被置空数据为：{}",preResult);
     *                             sink.next("");
     *                         }
     *                     }else {
     *                         //将每一步的执行结果发送出去
     *                         sink.next(currentStepResult);
     *                     }
     * @return 是否陷入循环
     */
    protected boolean isStuck() {
        //1.获取AI响应，包括工具调用的结果：AI 无思考结果。需要调用1个工具，工具名称：searchImageByKerWord，工具参数：{"query": "狸花猫"}。
        // 工具执行结果如下: "https://images.pexels.com/photos/33792531/pexels-photo-33792531.jpeg,......"
        List<String> aiResponseList = this.getResultList();
        //2.只有一条结果时无法比较
        if (aiResponseList.size() < 2) {
            return false;
        }
        //3.获取的List<String> resultList最后一条结果（最新的）
        String lastAiResponse = aiResponseList.get(aiResponseList.size() - 1);
        if (StrUtil.isEmpty(lastAiResponse)) {
            log.error("AI响应为空！");
            return false;
        }
        //4.计算重复内容出现次数
        int duplicateCount = 0;
        for (int i = aiResponseList.size() - 2; i >= 0; i--) {
            String aiResponse = aiResponseList.get(i);
            //5.将集合中的每条结果与最新的一条结果进行对比，如果内容相同  duplicateCount + 1
            if (StrUtil.equals(aiResponse, lastAiResponse)) {
                duplicateCount++;
            }
        }
        //6.如果重复内容出现次数大于等于重复执行阈值，则判定智能体陷入死循环状态
        boolean result = duplicateCount >= this.duplicateThreshold;
        if(result){
            //陷入循环
            log.error("智能体陷入死循环状态！");
            return true;
        }
        return false;
    }

    /**
     * 处理陷入循环的状态：通过设置 setNextStepPrompt + 设置执行状态位 AgentState.FINISHED + 设置最大执行步骤 三种方式结合，确保 AI 终止回合
     */
    protected void handleStuckState() {
        String newNextStepPrompt = "**检测到智能体陷入无限循环状态，立即终止你的回合，立即终止你的回合，立即终止你的回合！**";
        this.setNextStepPrompt(newNextStepPrompt);
        this.setState(AgentState.FINISHED);
        this.setMaxSteps(this.getResultList().size());
    }


    /**
     * 处理当前状态并决定下一步行动
     * @return 是否需要执行行动：true 需要，false 不需要
     */
    public abstract ReasonData reason();

    /**
     * 具体的执行方法：如何调用工具完成这些步骤任务
     * @return 执行结果
     */
    public abstract String act();

    /**
     * （同步）智能体的多步骤执行 Agent Loop，接收用户Prompt，通过多步骤执行生成最终回复
     *
     * @param userPrompt 用户 Prompt
     * @return 最终的执行结果
     */
    public abstract String run(String userPrompt);

    /**
     * （ SSE 流式输出）智能体的多步骤执行 Agent Loop，接收用户Prompt，通过多步骤执行生成最终回复。
     * 使用 SseEmitter 实现流式输出。
     *
     * @param userPrompt 用户 Prompt
     * @return SseEmitter
     */
    public abstract SseEmitter runStream(String userPrompt);

    /**
     * 执行单个步骤：先思考，再行动 并获取 AI 的响应结果（包括 AI 思考结果和工具调用结果）
     * @return 返回执行结果，由智能体做出下一步决策
     */
    @Override
    public String step() {
        try {
            //0.获取思考结果
            ReasonData reasonData = this.reason();
            boolean shouldAct = reasonData.isNeedInvokeTool();
            String reasonResult = reasonData.getReasonResult();
            //1.陷入循环，返回空数据
            if(isStuck()){
                handleStuckState();
                return "";
            }
            //2.根据思考结果判断是否需要行动
            //2.1无需行动
            if(!shouldAct){
                handleStuckState();
                return reasonResult;
            }
            //2.2需要行动
            String toolCallResult = this.act();
            if(StrUtil.isNotEmpty(toolCallResult)){
                return reasonResult + toolCallResult + "。";
            }else {
                return reasonResult + "需要调用工具，但工具返回结果为空。";
            }
        } catch (Exception e) {
            log.error("单步骤step()方法执行失败："+e.getMessage());
            return "单步骤step()方法执行失败"+e.getMessage();
        }
    }


}
